#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>

#include "network.h"
#include "request.h"
#include "database.h"
#include "process.h"


void process_request (struct ip_and_socket i_and_s) {
	char *req = malloc(2048);
    char *type;
	
	while (1) {

	    if (s_read (i_and_s.sockfd, req) == -1)
	        break;
    
        printf("\033[32m%s\033[39m\n",req);
        
        if (strlen(req) > 0) {
            
            type = malloc(strlen(req)+1);
            strcpy(type, req);

            switch (request_type(type)) {
                case ANNOUNCE:
	                client_request_announce (req, i_and_s); break;

                case LOOK:
	                client_request_look (req, i_and_s); break;

                case GETFILE:
	                client_request_getfile (req, i_and_s); break;

                case UPDATE:
	                client_request_update (req, i_and_s); break;
	                
	            default:
                    printf("wrong type\n");
                    break;
            }
        }
            
        print_database ();
	}

    printf("\033[32mclient logout\033[39m\n");
	close (i_and_s.sockfd);
}

// < announce listen $Port seed [$Filename1 $Length1 $PieceSize1 $Key1 $Filename2 $Length2 $PieceSize2 $Key2 …] leech [$Key3 $Key4 …]
// > ok
void client_request_announce (char *req, struct ip_and_socket i_and_s) {

	char **res = split (req);
	int i;
	
	// GET PEER INFOS
	i = 0;
	char *listen = res[++i];
	if (! equal(listen, "listen")) return;
	char *port = res[++i];
	if (port == NULL) return;

	struct peer *p = create_peer (i_and_s.ip, port);
	
	// SEED CASES
	char *seed = res[++i];
	if (! equal(seed, "seed")) return;
	else {
		char *elt = res[++i];
		if (! equal (elt, "[]")) {
			if (first_char(elt) != '[') return;
			else {
				elt = remove_first_char(elt);
				struct file *f;
				int last;
				char *name, *size, *block_size, *key;
				do {
					name = elt;
					if (name == NULL || last_char(name) == ']') return;
					
					size = res[++i];
					if (size == NULL || last_char(size) == ']') return;
			
					block_size = res[++i];
					if (block_size == NULL || last_char(block_size) == ']') return;
				
					key = res[++i];
					if (key == NULL) return;
					if (last_char(key) == ']') {
						key = remove_last_char (key);
						last = 1;
					}
					else {
						last = 0;
						elt = res[++i];	
					}
					
					f = create_file (name, size, block_size, key, 1);
					add_file_to_peer (f, p);
				} while (! last);
			}
		}
	}
	
	// LEECH FILES
	char *leech = res[++i];
	if (! equal(leech, "leech")) return;
	else {
		char *elt = res[++i];
		if (! equal (elt, "[]")) {
			if (first_char(elt) != '[') return;
			else {
				elt = remove_first_char(elt);
				struct file *f;
				int last;
				char *key;
				do {
					key = elt;
					if (key == NULL) return;
				
					if (last_char(key) == ']') {
						key = remove_last_char (key);
						last = 1;
					}
					else {
						last = 0;
						elt = res[++i];	
					}

					f = create_file ("-", "-", "-", key, 0);
					add_file_to_peer (f, p);
				} while (! last);
			}
		}
	}

	if (res[++i] != NULL) return;
	
	// ADD PEER TO DATABASE
	pthread_mutex_lock(&m_db);	
		struct peer *p_found = find_peer (p);
		if (p_found != NULL)
			*p_found = *p;
		else
			add_peer_to_database (p);
	pthread_mutex_unlock(&m_db);
	
	s_write (i_and_s.sockfd, "ok\n");
}

// < look [filename=”file_a.dat” filesize>”1048576”]
// > list [file_a.dat 2097152 1024 8905e92afeb80fc7722ec89eb0bf0966]
void client_request_look (char *req, struct ip_and_socket i_and_s) {
	if (db == NULL || db->nb_peer == 0) s_write (i_and_s.sockfd, "liiiiiiiiiiiist []\n");
	else {
		char **res = split (req);
		char * rep;
		int i;
		// RESPONSE INIT
		
		i = 0;
		char *look = res[i];
		if (! equal(look, "look")) return;

		char *elt = res[++i];
		rep = malloc (7 * sizeof (char));
		strcpy (rep, "list ["); 
		
		// look [] CASE 
		if (equal (elt, "[]")) {
			if (db != NULL) {
				pthread_mutex_lock(&m_db);	
					rep = strcpy (rep, look_with_criterions (rep, NULL));
				pthread_mutex_unlock(&m_db);	
			}
		}
		//  look [blabla...] CASE
		else {
			if (first_char(elt) != '[') return;
			else {
	
				int last = 0;

				struct criterion **ls = new_criterion_tab (); 
				elt = remove_first_char (elt); 

				// CRITERIONS SAVE
				
				do {
					if (last_char (elt) == ']') {
						elt = remove_last_char (elt);
						last = 1;
					}
					
					struct criterion *c = new_criterion ();
					c = parse_criterion (c, elt); 
					ls = add_criterion (ls, c); 

					if (!last)
						elt = res[++i];	

				} while (!last);
                
                
				// RESPONSE WRITING							
				pthread_mutex_lock(&m_db);	
				    rep = strcpy (rep, look_with_criterions (rep, ls));
				pthread_mutex_unlock(&m_db);	

                //free_criterion_list (ls);
   			}
		}

		strcat (rep, "]\n");
		s_write (i_and_s.sockfd, rep);
	}

}

// < getfile 8905e92afeb80fc7722ec89eb0bf0966
// > peers 8905e92afeb80fc7722ec89eb0bf0966 [1.1.1.2:2222 1.1.1.3:3333]
void client_request_getfile (char *req, struct ip_and_socket i_and_s) {

	char **res = split (req);
	char * rep;
	int i;

	// RESPONSE INIT
	i = 0;
	char *getfile = res[i];
	if (! equal(getfile, "getfile")) return;

	char *key = res[++i];
	if (key == NULL) return;

	rep = malloc ((10 + strlen (key)) * sizeof (char));
	strcpy (rep, "peers "); 
	strcat (rep, key);
	strcat (rep, " [");

	//FIND PEERS HAVING THE FILE
	if (db == NULL || db->nb_peer == 0);
	else {
		pthread_mutex_lock(&m_db);	
			strcpy (rep, find_peers_getfile (key, rep));
		pthread_mutex_unlock(&m_db);	
	}

	strcat (rep, "]\n");
	s_write (i_and_s.sockfd, rep);
}

// < update seed [] leech [8905e92afeb80fc7722ec89eb0bf0966]
// > ok
void client_request_update (char *req, struct ip_and_socket i_and_s) {

	struct peer * p_curent, *p_new;
	char **res = split (req);
	int i;

	// UPDATE CHECK	
	i = 0;
	char *update = res[i++];
	if (! equal(update, "update")) return;
	
	p_curent = find_peer_with_ip (i_and_s);
	p_new = create_peer (p_curent->ip, p_curent->port);
		
	
	// SEED CASE
	char *seed = res[i++];
	if (! equal(seed, "seed")) return;
	else {
		char *key = res[i++];
		if (! equal (key, "[]")) {
			if (first_char(key) != '[') return;
			else {
				key = remove_first_char(key);
				int last;
				do {
					if (key == NULL) return;
					if (last_char(key) == ']') {
						key = remove_last_char (key);
						pthread_mutex_lock(&m_db);	
							update_seed (p_curent, p_new, key);
						pthread_mutex_unlock(&m_db);	
						last = 1;
					}
					else {
						pthread_mutex_lock(&m_db);	
							update_seed (p_curent, p_new, key);
						pthread_mutex_unlock(&m_db);	
						key = res[i++];	
						last = 0;
					}
		
				} while (! last);
			}
		}
	}
	
	// LEECH CASE
	char *leech = res[i++];
	if (! equal(leech, "leech")) return;
	else {
		char *key;
		key = res[i++];
		if (! equal (key, "[]")) {
			if (first_char(key) != '[') return;
			else {
				key = remove_first_char(key);
				int last;
				do {
					if (key == NULL) return;
					if (last_char(key) == ']') {
						key = remove_last_char (key);
						update_leech (p_new, key);	
						last = 1;
					}
					else {
						update_leech (p_new, key);	
						key = res[i++];	
						last = 0;
					}


				} while (! last);
			}
		}
	}

	if (res[i++] != NULL) return;

	pthread_mutex_lock(&m_db);	
		replace_peers (p_curent, p_new); 
	pthread_mutex_unlock(&m_db);	
	
	s_write (i_and_s.sockfd, "ok\n");
}
